/**
 * @file class.h
 * @author <raphael.londeix@gmail.com> Raphaël Londeix
 *
 * @section DESCRIPTION
 * @FIXME@
 */

#ifndef __CLASS_H__
# define __CLASS_H__

# include <stdarg.h>
# include <sys/types.h>
# ifndef NULL
#  define NULL ((void*) 0x0)
# endif

# include "bool.h"
# include "object.h"

typedef void (*ctor_t)(Object* this, va_list* args);
typedef void (*dtor_t)(Object* this);
typedef char const* (*str_t)(Object const* this);
typedef Object* (*binary_operator_t)(Object const* this, Object const* other);
typedef bool (*binary_comparator_t)(Object const* this, Object const* other);

struct Class;
typedef struct Class Class;

typedef struct MetaClass MetaClass;
struct MetaClass
{
    Class const* const*         __base__;  // parent meta class
    Class const* const*         __class__; // class description (attributes to malloc & memcpy)
    size_t                      __size__;  // size of class description
    char const* const           __name__;  // name of the class
    ctor_t                      __init__;  // Constructor
    dtor_t                      __del__;   // Destructor
    str_t                       __str__;   // to string method
    binary_operator_t           __add__;
    binary_operator_t           __sub__;
    binary_operator_t           __mul__;
    binary_operator_t           __odiv__;
    binary_comparator_t         __eq__;
    binary_comparator_t         __gt__;
    binary_comparator_t         __lt__;
};

struct Class
{
    MetaClass const* const __metaclass__;
};

#define typeof(instance)        (((Class*) (instance))->__metaclass__)
#define get_parent_type(type)   ((type)->__base__ != NULL && *((type)->__base__) != NULL ? \
                                (*(type)->__base__)->__metaclass__ : NULL)
#define get_parent(instance)    ((*(typeof(instance)->__base__))->__metaclass__)
#define has_parent(instance)    (get_parent(instance) != NULL)
#define get_parent_instance(instance) up_cast(instance, get_parent(instance))

Object const* up_cast(Object const* object, MetaClass const* search_for);
char const* str(Object const* object);
Object* add(Object const* o1, Object const* o2);
Object* sub(Object const* o1, Object const* o2);
Object* mul(Object const* o1, Object const* o2);
Object* odiv(Object const* o1, Object const* o2);

#define eq(this, other) \
({ \
    assert(typeof(this) == typeof(other)); \
    MetaClass const* type = typeof(this); \
    if (type->__eq__ == NULL) \
        raise("Class '", type->__name__, "' has no eq method"); \
    type->__eq__(this, other); \
})

#define gt(this, other) \
({ \
    assert(typeof(this) == typeof(other)); \
    MetaClass const* type = typeof(this); \
    if (type->__gt__ == NULL) \
        raise("Class '", type->__name__, "' has no eq method"); \
    type->__gt__(this, other); \
})

#define lt(this, other) \
({ \
    assert(typeof(this) == typeof(other)); \
    MetaClass const* type = typeof(this); \
    if (type->__lt__ == NULL) \
        raise("Class '", type->__name__, "' has no eq method"); \
    type->__lt__(this, other); \
})
// Helper macro
// example:
// static const MetaClass my_metaclass = IMPL_METACLASS(...);
// base is NULL or a pointer to the parent class
// descr is a pointer to the class representation (instances description)
# define IMPL_METACLASS(base, descr, size, name, \
                        ctor, dtor, str, \
                        add, sub, mul, div, \
                        eq, gt, lt) \
{ \
    (Class const* const*) base, \
    (Class const* const*) descr, \
    size, \
    name, \
    (ctor_t)            ctor, \
    (dtor_t)            dtor, \
    (str_t)             str, \
    (binary_operator_t) add, \
    (binary_operator_t) sub, \
    (binary_operator_t) mul, \
    (binary_operator_t) div, \
    (binary_comparator_t) eq, \
    (binary_comparator_t) gt, \
    (binary_comparator_t) lt, \
}

///////////////////////////////////////////////
#define BEGIN_CLASS(cls) \
typedef struct cls##Class cls##Class; \
struct cls##Class \
{ \
    Class __class__
///////////////////////////////////////////////


///////////////////////////////////////////////
#define END_CLASS(cls) \
}
//////////////////////////////////////////////

# define IMPL_CLASS(cls, base, ctor, dtor, str, \
                    add, sub, mul, div, \
                    eq, gt, lt, ...) \
static MetaClass const __##cls##MetaClass = IMPL_METACLASS(\
        base, \
        &cls, \
        sizeof(cls##Class),\
        #cls, \
        ctor, dtor, str, \
        add, sub, mul, div, \
        eq, gt, lt \
); \
cls##Class __##cls##Description = {{&__##cls##MetaClass}, __VA_ARGS__}; \
Class const* const cls = (Class const* const) &__##cls##Description

#endif /* !__CLASS_H__ */

